﻿namespace SkyDean.FareLiz.WinForm.Components.Controls.DatePicker.Helper
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Linq;

    using SkyDean.FareLiz.WinForm.Components.Controls.DatePicker.Interfaces;

    /// <summary>Class for calendar methods.</summary>
    internal static class DateMethods
    {
        /// <summary>
        /// Gets the days in month for the specified date.
        /// </summary>
        /// <param name="date">
        /// The <see cref="MonthCalendarDate"/> to get the days in month for.
        /// </param>
        /// <returns>
        /// The number of days in the month.
        /// </returns>
        public static int GetDaysInMonth(MonthCalendarDate date)
        {
            return GetDaysInMonth(date.Calendar, date.Year, date.Month);
        }

        /// <summary>
        /// Gets the days in month for the specified year and month using the specified <paramref name="cal"/>.
        /// </summary>
        /// <param name="cal">
        /// The <see cref="Calendar"/> to use.
        /// </param>
        /// <param name="year">
        /// The year value.
        /// </param>
        /// <param name="month">
        /// The month value.
        /// </param>
        /// <returns>
        /// The number of days in the month using the specified <see cref="Calendar"/>.
        /// </returns>
        public static int GetDaysInMonth(Calendar cal, int year, int month)
        {
            return (cal ?? CultureInfo.CurrentUICulture.Calendar).GetDaysInMonth(year, month);
        }

        /// <summary>
        /// Gets the week of the year for the specified date, culture and calendar.
        /// </summary>
        /// <param name="info">
        /// The <see cref="CultureInfo"/> to use.
        /// </param>
        /// <param name="cal">
        /// The <see cref="Calendar"/> to use.
        /// </param>
        /// <param name="date">
        /// The date value to get the week of year for.
        /// </param>
        /// <returns>
        /// The week of the year.
        /// </returns>
        public static int GetWeekOfYear(CultureInfo info, Calendar cal, DateTime date)
        {
            var ci = info ?? CultureInfo.CurrentUICulture;
            var c = cal ?? ci.Calendar;

            return c.GetWeekOfYear(date, ci.DateTimeFormat.CalendarWeekRule, ci.DateTimeFormat.FirstDayOfWeek);
        }

        /// <summary>
        /// Gets the abbreviated and day names, beginning with the first day provided by the <paramref name="provider"/>.
        /// </summary>
        /// <param name="provider">
        /// The <see cref="ICustomFormatProvider"/> to use.
        /// </param>
        /// <returns>
        /// The abbreviated and day names beginning with the first day.
        /// </returns>
        public static string[,] GetDayNames(ICustomFormatProvider provider)
        {
            var abbDayNames = new List<string>(provider.AbbreviatedDayNames);
            var shortestDayNames = new List<string>(provider.ShortestDayNames);
            var dayNames = new List<string>(provider.DayNames);
            var firstDayName = provider.GetAbbreviatedDayName(provider.FirstDayOfWeek);

            var firstNameIndex = abbDayNames.IndexOf(firstDayName);

            var names = new string[3, dayNames.Count];
            var j = 0;

            for (var i = firstNameIndex; i < abbDayNames.Count; i++, j++)
            {
                names[0, j] = dayNames[i];
                names[1, j] = abbDayNames[i];
                names[2, j] = shortestDayNames[i];
            }

            for (var i = 0; i < firstNameIndex; i++, j++)
            {
                names[0, j] = dayNames[i];
                names[1, j] = abbDayNames[i];
                names[2, j] = shortestDayNames[i];
            }

            return names;
        }

        /// <summary>
        /// Gets the abbreviated or day names, beginning with the first day of the week specified by the
        /// <paramref name="provider"/>.
        /// </summary>
        /// <param name="provider">
        /// The provider to use.
        /// </param>
        /// <param name="index">
        /// The index for the kind of name : 0 means full, 1 means abbreviated and 2 means shortest day names.
        /// </param>
        /// <returns>
        /// The (abbreviated) day names.
        /// </returns>
        public static string[] GetDayNames(ICustomFormatProvider provider, int index)
        {
            if (index < 0 || index > 2)
            {
                index = 0;
            }

            var dayNames = GetDayNames(provider);

            var names = new string[dayNames.GetLength(1)];

            for (var i = 0; i < names.Length; i++)
            {
                names[i] = dayNames[index, i];
            }

            return names;
        }

        /// <summary>
        /// Gets from the specified <see cref="CalendarDayOfWeek"/> a generic list with the work days.
        /// </summary>
        /// <param name="nonWorkDays">
        /// The non working days.
        /// </param>
        /// <returns>
        /// A list with the working days.
        /// </returns>
        public static List<DayOfWeek> GetWorkDays(CalendarDayOfWeek nonWorkDays)
        {
            var workDays = new List<DayOfWeek>();

            for (var i = 0; i < 7; i++)
            {
                workDays.Add((DayOfWeek)i);
            }

            GetSysDaysOfWeek(nonWorkDays).ForEach(
                day =>
                    {
                        if (workDays.Contains(day))
                        {
                            workDays.Remove(day);
                        }
                    });

            return workDays;
        }

        /// <summary>
        /// Gets a value indicating whether the specified culture is a right to left culture.
        /// </summary>
        /// <param name="info">
        /// The culture.
        /// </param>
        /// <returns>
        /// true, if RTL, false otherwise.
        /// </returns>
        public static bool IsRTLCulture(CultureInfo info)
        {
            return info.TextInfo.IsRightToLeft;
        }

        /// <summary>Gets a value indicating whether the current UI culture is a right to left culture.</summary>
        /// <returns>true, if RTL, false otherwise.</returns>
        public static bool IsRTLCulture()
        {
            return CultureInfo.CurrentUICulture.TextInfo.IsRightToLeft;
        }

        /// <summary>
        /// Gets a <see cref="string"/> representing the <paramref name="number"/>. If <paramref name="nativeDigits"/>
        /// is not <c>null</c> and valid the numbers in <paramref name="number"/>
        /// are converted using the native digits.
        /// </summary>
        /// <param name="number">
        /// The number to convert.
        /// </param>
        /// <param name="nativeDigits">
        /// The native digits, can be <c>null</c>.
        /// </param>
        /// <param name="prefixZero">
        /// if set to <c>true</c> prefixes the string with a zero.
        /// </param>
        /// <returns>
        /// A <see cref="string"/> representing the <paramref name="number"/> either in arabic numerals or if specified using the native digits.
        /// </returns>
        public static string GetNumberString(int number, string[] nativeDigits, bool prefixZero)
        {
            if (nativeDigits == null || nativeDigits.Length != 10)
            {
                return prefixZero ? number.ToString("##00") : number.ToString(CultureInfo.CurrentUICulture);
            }

            return GetNativeNumberString(number, nativeDigits, prefixZero);
        }

        /// <summary>
        /// Gets a string where each number in <paramref name="number"/> is replaced with the native number representation as specified by
        /// <paramref name="nativeDigits"/>.
        /// </summary>
        /// <param name="number">
        /// The number to convert.
        /// </param>
        /// <param name="nativeDigits">
        /// The native digits.
        /// </param>
        /// <param name="prefixZero">
        /// if set to <c>true</c> and the <paramref name="number"/> is greater 0 and less than 10, prefixes the string with the native
        /// zero.
        /// </param>
        /// <returns>
        /// A <see cref="string"/> representing the <paramref name="number"/> using the native digits specified by
        /// <paramref name="nativeDigits"/>.
        /// </returns>
        public static string GetNativeNumberString(int number, string[] nativeDigits, bool prefixZero)
        {
            if (nativeDigits == null || nativeDigits.Length != 10)
            {
                return string.Empty;
            }

            if (number > -1 && number < 10)
            {
                return prefixZero ? nativeDigits[0] + nativeDigits[number] : nativeDigits[number];
            }

            var list = new List<int>();

            while (number != 0)
            {
                list.Insert(0, number % 10);
                number = number / 10;
            }

            return list.ConvertAll(i => nativeDigits[i]).Aggregate((s1, s2) => s1 + s2);
        }

        /// <summary>
        /// Gets a generic list of <see cref="DayOfWeek"/> value from the specified <see cref="CalendarDayOfWeek"/>
        /// value.
        /// </summary>
        /// <param name="days">
        /// The <see cref="CalendarDayOfWeek"/> value.
        /// </param>
        /// <returns>
        /// A generic list of type <see cref="DayOfWeek"/>.
        /// </returns>
        private static List<DayOfWeek> GetSysDaysOfWeek(CalendarDayOfWeek days)
        {
            var list = new List<DayOfWeek>();

            if ((days & CalendarDayOfWeek.Monday) == CalendarDayOfWeek.Monday)
            {
                list.Add(DayOfWeek.Monday);
            }

            if ((days & CalendarDayOfWeek.Tuesday) == CalendarDayOfWeek.Tuesday)
            {
                list.Add(DayOfWeek.Tuesday);
            }

            if ((days & CalendarDayOfWeek.Wednesday) == CalendarDayOfWeek.Wednesday)
            {
                list.Add(DayOfWeek.Wednesday);
            }

            if ((days & CalendarDayOfWeek.Thursday) == CalendarDayOfWeek.Thursday)
            {
                list.Add(DayOfWeek.Thursday);
            }

            if ((days & CalendarDayOfWeek.Friday) == CalendarDayOfWeek.Friday)
            {
                list.Add(DayOfWeek.Friday);
            }

            if ((days & CalendarDayOfWeek.Saturday) == CalendarDayOfWeek.Saturday)
            {
                list.Add(DayOfWeek.Saturday);
            }

            if ((days & CalendarDayOfWeek.Sunday) == CalendarDayOfWeek.Sunday)
            {
                list.Add(DayOfWeek.Sunday);
            }

            return list;
        }
    }
}